/*
 * Copyright 2005-2015, Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Axel Dörfler, axeld@pinc-software.de
 *		Michael Lotz <mmlr@mlotz.ch>
 */


#include <MessageAdapter.h>
#include <MessagePrivate.h>
#include <MessageUtils.h>

#include <stdlib.h>


namespace BPrivate {

#define R5_MESSAGE_FLAG_VALID			0x01
#define R5_MESSAGE_FLAG_INCLUDE_TARGET	0x02
#define R5_MESSAGE_FLAG_INCLUDE_REPLY	0x04
#define R5_MESSAGE_FLAG_SCRIPT_MESSAGE	0x08

#define R5_FIELD_FLAG_VALID				0x01
#define R5_FIELD_FLAG_MINI_DATA			0x02
#define R5_FIELD_FLAG_FIXED_SIZE		0x04
#define R5_FIELD_FLAG_SINGLE_ITEM		0x08


enum {
	SECTION_MESSAGE_HEADER = 'FOB2',
	SECTION_OFFSET_TABLE = 'STof',
	SECTION_TARGET_INFORMATION = 'ENwh',
	SECTION_SINGLE_ITEM_DATA = 'SGDa',
	SECTION_FIXED_SIZE_ARRAY_DATA = 'FADa',
	SECTION_VARIABLE_SIZE_ARRAY_DATA = 'VADa',
	SECTION_SORTED_INDEX_TABLE = 'DXIn',
	SECTION_END_OF_DATA = 'DDEn'
};


struct r5_message_header {
	uint32	magic;
	uint32	checksum;
	int32	flattened_size;
	int32	what;
	uint8	flags;
} _PACKED;


struct dano_section_header {
	uint32		code;
	int32		size;
	uint8		data[0];
} _PACKED;


struct dano_message_header {
	int32		what;
	int32		padding;
} _PACKED;


typedef struct offset_table_s {
	int32		indexTable;
	int32		endOfData;
	int64		padding;
} OffsetTable;


struct dano_single_item {
	type_code	type;
	int32		item_size;
	uint8		name_length;
	char		name[0];
} _PACKED;


struct dano_fixed_size_array {
	type_code	type;
	int32		size_per_item;
	uint8		name_length;
	char		name[0];
} _PACKED;


struct dano_variable_size_array {
	type_code	type;
	int32		padding;
	uint8		name_length;
	char		name[0];
} _PACKED;


inline int32
pad_to_8(int32 value)
{
	return (value + 7) & ~7;
}


/*static*/ ssize_t
MessageAdapter::FlattenedSize(uint32 format, const BMessage *from)
{
	switch (format) {
		case MESSAGE_FORMAT_R5:
		case MESSAGE_FORMAT_R5_SWAPPED:
			return _R5FlattenedSize(from);
	}

	return -1;
}


/*static*/ status_t
MessageAdapter::Flatten(uint32 format, const BMessage *from, char *buffer,
	ssize_t *size)
{
	switch (format) {
		case MESSAGE_FORMAT_R5:
		case MESSAGE_FORMAT_R5_SWAPPED:
			return _FlattenR5Message(format, from, buffer, size);
	}

	return B_ERROR;
}


/*static*/ status_t
MessageAdapter::Flatten(uint32 format, const BMessage *from, BDataIO *stream,
	ssize_t *size)
{
	switch (format) {
		case MESSAGE_FORMAT_R5:
		case MESSAGE_FORMAT_R5_SWAPPED:
		{
			ssize_t flattenedSize = _R5FlattenedSize(from);
			char *buffer = (char *)malloc(flattenedSize);
			if (!buffer)
				return B_NO_MEMORY;

			status_t result = _FlattenR5Message(format, from, buffer,
				&flattenedSize);
			if (result < B_OK) {
				free(buffer);
				return result;
			}

			ssize_t written = stream->Write(buffer, flattenedSize);
			if (written != flattenedSize) {
				free(buffer);
				return (written >= 0 ? B_ERROR : written);
			}

			if (size)
				*size = flattenedSize;

			free(buffer);
			return B_OK;
		}
	}

	return B_ERROR;
}


/*static*/ status_t
MessageAdapter::Unflatten(uint32 format, BMessage *into, const char *buffer)
{
	if (format == KMessage::kMessageHeaderMagic) {
		KMessage message;
		status_t result = message.SetTo(buffer,
			((KMessage::Header *)buffer)->size);
		if (result != B_OK)
			return result;

		return _ConvertFromKMessage(&message, into);
	}

	try {
		switch (format) {
			case MESSAGE_FORMAT_R5:
			{
				r5_message_header *header = (r5_message_header *)buffer;
				BMemoryIO stream(buffer + sizeof(uint32),
					header->flattened_size - sizeof(uint32));
				return _UnflattenR5Message(format, into, &stream);
			}

			case MESSAGE_FORMAT_R5_SWAPPED:
			{
				r5_message_header *header = (r5_message_header *)buffer;
				BMemoryIO stream(buffer + sizeof(uint32),
					__swap_int32(header->flattened_size) - sizeof(uint32));
				return _UnflattenR5Message(format, into, &stream);
			}

			case MESSAGE_FORMAT_DANO:
			case MESSAGE_FORMAT_DANO_SWAPPED:
			{
				dano_section_header *header = (dano_section_header *)buffer;
				ssize_t size = header->size;
				if (header->code == MESSAGE_FORMAT_DANO_SWAPPED)
					size = __swap_int32(size);

				BMemoryIO stream(buffer + sizeof(uint32), size - sizeof(uint32));
				return _UnflattenDanoMessage(format, into, &stream);
			}
		}
	} catch (status_t error) {
		into->MakeEmpty();
		return error;
	}

	return B_NOT_A_MESSAGE;
}


/*static*/ status_t
MessageAdapter::Unflatten(uint32 format, BMessage *into, BDataIO *stream)
{
	try {
		switch (format) {
			case MESSAGE_FORMAT_R5:
			case MESSAGE_FORMAT_R5_SWAPPED:
				return _UnflattenR5Message(format, into, stream);

			case MESSAGE_FORMAT_DANO:
			case MESSAGE_FORMAT_DANO_SWAPPED:
				return _UnflattenDanoMessage(format, into, stream);
		}
	} catch (status_t error) {
		into->MakeEmpty();
		return error;
	}

	return B_NOT_A_MESSAGE;
}


/*static*/ status_t
MessageAdapter::ConvertToKMessage(const BMessage* from, KMessage& to)
{
	if (from == NULL)
		return B_BAD_VALUE;

	BMessage::Private fromPrivate(const_cast<BMessage*>(from));
	BMessage::message_header* header = fromPrivate.GetMessageHeader();
	uint8* data = fromPrivate.GetMessageData();

	// Iterate through the fields and import them in the target message
	BMessage::field_header* field = fromPrivate.GetMessageFields();
	for (uint32 i = 0; i < header->field_count; i++, field++) {
		const char* name = (const char*)data + field->offset;
		const uint8* fieldData = data + field->offset + field->name_length;
		bool fixedSize = (field->flags & FIELD_FLAG_FIXED_SIZE) != 0;

		if (fixedSize) {
			status_t status = to.AddArray(name, field->type, fieldData,
				field->data_size / field->count, field->count);
			if (status != B_OK)
				return status;
		} else {
			for (uint32 i = 0; i < field->count; i++) {
				uint32 itemSize = *(uint32*)fieldData;
				fieldData += sizeof(uint32);
				status_t status = to.AddData(name, field->type, fieldData,
					itemSize, false);
				if (status != B_OK)
					return status;
				fieldData += itemSize;
			}
		}
	}
	return B_OK;
}


/*static*/ status_t
MessageAdapter::_ConvertFromKMessage(const KMessage *fromMessage,
	BMessage *toMessage)
{
	if (!fromMessage || !toMessage)
		return B_BAD_VALUE;

	// make empty and init what of the target message
	toMessage->MakeEmpty();
	toMessage->what = fromMessage->What();

	BMessage::Private toPrivate(toMessage);
	toPrivate.SetTarget(fromMessage->TargetToken());
	toPrivate.SetReply(B_SYSTEM_TEAM, fromMessage->ReplyPort(),
		fromMessage->ReplyToken());
	if (fromMessage->ReplyPort() >= 0) {
		toPrivate.GetMessageHeader()->flags |= MESSAGE_FLAG_REPLY_AS_KMESSAGE
			| MESSAGE_FLAG_REPLY_REQUIRED;
	}

	// Iterate through the fields and import them in the target message
	KMessageField field;
	while (fromMessage->GetNextField(&field) == B_OK) {
		int32 elementCount = field.CountElements();
		if (elementCount > 0) {
			for (int32 i = 0; i < elementCount; i++) {
				int32 size;
				const void *data = field.ElementAt(i, &size);
				status_t result;

				if (field.TypeCode() == B_MESSAGE_TYPE) {
					// message type: if it's a KMessage, convert it
					KMessage message;
					if (message.SetTo(data, size) == B_OK) {
						BMessage bMessage;
						result = _ConvertFromKMessage(&message, &bMessage);
						if (result < B_OK)
							return result;

						result = toMessage->AddMessage(field.Name(), &bMessage);
					} else {
						// just add it
						result = toMessage->AddData(field.Name(),
							field.TypeCode(), data, size,
							field.HasFixedElementSize(), 1);
					}
				} else {
					result = toMessage->AddData(field.Name(), field.TypeCode(),
						data, size, field.HasFixedElementSize(), 1);
				}

				if (result < B_OK)
					return result;
			}
		}
	}

	return B_OK;
}


/*static*/ ssize_t
MessageAdapter::_R5FlattenedSize(const BMessage *from)
{
	BMessage::Private messagePrivate((BMessage *)from);
	BMessage::message_header* header = messagePrivate.GetMessageHeader();

	// header size (variable, depending on the flags)

	ssize_t flattenedSize = sizeof(r5_message_header);

	if (header->target != B_NULL_TOKEN)
		flattenedSize += sizeof(int32);

	if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN
		&& header->reply_team >= 0) {
		// reply info + big flags
		flattenedSize += sizeof(port_id) + sizeof(int32) + sizeof(team_id) + 4;
	}

	// field size

	uint8 *data = messagePrivate.GetMessageData();
	BMessage::field_header *field = messagePrivate.GetMessageFields();
	for (uint32 i = 0; i < header->field_count; i++, field++) {
		// flags and type
		flattenedSize += 1 + sizeof(type_code);

#if 0
		bool miniData = field->dataSize <= 255 && field->count <= 255;
#else
		// TODO: we don't know the R5 dataSize yet (padding)
		bool miniData = false;
#endif

		// item count
		if (field->count > 1)
			flattenedSize += (miniData ? sizeof(uint8) : sizeof(uint32));

		// data size
		flattenedSize += (miniData ? sizeof(uint8) : sizeof(size_t));

		// name length and name
		flattenedSize += 1 + min_c(field->name_length - 1, 255);

		// data
		if (field->flags & FIELD_FLAG_FIXED_SIZE)
			flattenedSize += field->data_size;
		else {
			uint8 *source = data + field->offset + field->name_length;

			for (uint32 i = 0; i < field->count; i++) {
				ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t);
				flattenedSize += pad_to_8(itemSize);
				source += itemSize;
			}
		}
	}

	// pseudo field with flags 0
	return flattenedSize + 1;
}


/*static*/ status_t
MessageAdapter::_FlattenR5Message(uint32 format, const BMessage *from,
	char *buffer, ssize_t *size)
{
	BMessage::Private messagePrivate((BMessage *)from);
	BMessage::message_header *header = messagePrivate.GetMessageHeader();
	uint8 *data = messagePrivate.GetMessageData();

	r5_message_header *r5header = (r5_message_header *)buffer;
	uint8 *pointer = (uint8 *)buffer + sizeof(r5_message_header);

	r5header->magic = MESSAGE_FORMAT_R5;
	r5header->what = from->what;
	r5header->checksum = 0;

	uint8 flags = R5_MESSAGE_FLAG_VALID;
	if (header->target != B_NULL_TOKEN) {
		*(int32 *)pointer = header->target;
		pointer += sizeof(int32);
		flags |= R5_MESSAGE_FLAG_INCLUDE_TARGET;
	}

	if (header->reply_port >= 0 && header->reply_target != B_NULL_TOKEN
		&& header->reply_team >= 0) {
		// reply info
		*(port_id *)pointer = header->reply_port;
		pointer += sizeof(port_id);
		*(int32 *)pointer = header->reply_target;
		pointer += sizeof(int32);
		*(team_id *)pointer = header->reply_team;
		pointer += sizeof(team_id);

		// big flags
		*pointer = (header->reply_target == B_PREFERRED_TOKEN ? 1 : 0);
		pointer++;

		*pointer = (header->flags & MESSAGE_FLAG_REPLY_REQUIRED ? 1 : 0);
		pointer++;

		*pointer = (header->flags & MESSAGE_FLAG_REPLY_DONE ? 1 : 0);
		pointer++;

		*pointer = (header->flags & MESSAGE_FLAG_IS_REPLY ? 1 : 0);
		pointer++;

		flags |= R5_MESSAGE_FLAG_INCLUDE_REPLY;
	}

	if (header->flags & MESSAGE_FLAG_HAS_SPECIFIERS)
		flags |= R5_MESSAGE_FLAG_SCRIPT_MESSAGE;

	r5header->flags = flags;

	// store the header size - used for the checksum later
	ssize_t headerSize = (addr_t)pointer - (addr_t)buffer;

	// collect and add the data
	BMessage::field_header *field = messagePrivate.GetMessageFields();
	for (uint32 i = 0; i < header->field_count; i++, field++) {
		flags = R5_FIELD_FLAG_VALID;

		if (field->count == 1)
			flags |= R5_FIELD_FLAG_SINGLE_ITEM;
		// TODO: we don't really know the data size now (padding missing)
//		if (field->data_size <= 255 && field->count <= 255)
//			flags |= R5_FIELD_FLAG_MINI_DATA;
		if (field->flags & FIELD_FLAG_FIXED_SIZE)
			flags |= R5_FIELD_FLAG_FIXED_SIZE;

		*pointer = flags;
		pointer++;

		*(type_code *)pointer = field->type;
		pointer += sizeof(type_code);

		if (!(flags & R5_FIELD_FLAG_SINGLE_ITEM)) {
			if (flags & R5_FIELD_FLAG_MINI_DATA) {
				*pointer = (uint8)field->count;
				pointer++;
			} else {
				*(int32 *)pointer = field->count;
				pointer += sizeof(int32);
			}
		}

		// we may have to adjust this to account for padding later
		uint8 *fieldSize = pointer;
		if (flags & R5_FIELD_FLAG_MINI_DATA) {
			*pointer = (uint8)field->data_size;
			pointer++;
		} else {
			*(ssize_t *)pointer = field->data_size;
			pointer += sizeof(ssize_t);
		}

		// name
		int32 nameLength = min_c(field->name_length - 1, 255);
		*pointer = (uint8)nameLength;
		pointer++;

		strncpy((char *)pointer, (char *)data + field->offset, nameLength);
		pointer += nameLength;

		// data
		uint8 *source = data + field->offset + field->name_length;
		if (flags & R5_FIELD_FLAG_FIXED_SIZE) {
			memcpy(pointer, source, field->data_size);
			pointer += field->data_size;
		} else {
			uint8 *previous = pointer;
			for (uint32 i = 0; i < field->count; i++) {
				ssize_t itemSize = *(ssize_t *)source + sizeof(ssize_t);
				memcpy(pointer, source, itemSize);
				ssize_t paddedSize = pad_to_8(itemSize);
				memset(pointer + itemSize, 0, paddedSize - itemSize);
				pointer += paddedSize;
				source += itemSize;
			}

			// adjust the field size to the padded value
			if (flags & R5_FIELD_FLAG_MINI_DATA)
				*fieldSize = (uint8)(pointer - previous);
			else
				*(ssize_t *)fieldSize = (pointer - previous);
		}
	}

	// terminate the fields with a pseudo field with flags 0 (not valid)
	*pointer = 0;
	pointer++;

	// calculate the flattened size from the pointers
	r5header->flattened_size = (addr_t)pointer - (addr_t)buffer;
	r5header->checksum = CalculateChecksum((uint8 *)(buffer + 8),
		headerSize - 8);

	if (size)
		*size = r5header->flattened_size;

	return B_OK;
}


/*static*/ status_t
MessageAdapter::_UnflattenR5Message(uint32 format, BMessage *into,
	BDataIO *stream)
{
	into->MakeEmpty();

	BMessage::Private messagePrivate(into);
	BMessage::message_header *header = messagePrivate.GetMessageHeader();

	TReadHelper reader(stream);
	if (format == MESSAGE_FORMAT_R5_SWAPPED)
		reader.SetSwap(true);

	// the stream is already advanced by the size of the "format"
	r5_message_header r5header;
	reader(((uint8 *)&r5header) + sizeof(uint32),
		sizeof(r5header) - sizeof(uint32));

	header->what = into->what = r5header.what;
	if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_TARGET)
		reader(&header->target, sizeof(header->target));

	if (r5header.flags & R5_MESSAGE_FLAG_INCLUDE_REPLY) {
		// reply info
		reader(&header->reply_port, sizeof(header->reply_port));
		reader(&header->reply_target, sizeof(header->reply_target));
		reader(&header->reply_team, sizeof(header->reply_team));

		// big flags
		uint8 bigFlag;
		reader(bigFlag);
		if (bigFlag)
			header->reply_target = B_PREFERRED_TOKEN;

		reader(bigFlag);
		if (bigFlag)
			header->flags |= MESSAGE_FLAG_REPLY_REQUIRED;

		reader(bigFlag);
		if (bigFlag)
			header->flags |= MESSAGE_FLAG_REPLY_DONE;

		reader(bigFlag);
		if (bigFlag)
			header->flags |= MESSAGE_FLAG_IS_REPLY;
	}

	if (r5header.flags & R5_MESSAGE_FLAG_SCRIPT_MESSAGE)
		header->flags |= MESSAGE_FLAG_HAS_SPECIFIERS;

	uint8 flags;
	reader(flags);
	while ((flags & R5_FIELD_FLAG_VALID) != 0) {
		bool fixedSize = flags & R5_FIELD_FLAG_FIXED_SIZE;
		bool miniData = flags & R5_FIELD_FLAG_MINI_DATA;
		bool singleItem = flags & R5_FIELD_FLAG_SINGLE_ITEM;

		type_code type;
		reader(type);

		int32 itemCount;
		if (!singleItem) {
			if (miniData) {
				uint8 miniCount;
				reader(miniCount);
				itemCount = miniCount;
			} else
				reader(itemCount);
		} else
			itemCount = 1;

		int32 dataSize;
		if (miniData) {
			uint8 miniSize;
			reader(miniSize);
			dataSize = miniSize;
		} else
			reader(dataSize);

		if (dataSize <= 0)
			return B_ERROR;

		// name
		uint8 nameLength;
		reader(nameLength);

		char nameBuffer[256];
		reader(nameBuffer, nameLength);
		nameBuffer[nameLength] = '\0';

		uint8 *buffer = (uint8 *)malloc(dataSize);
		uint8 *pointer = buffer;
		reader(buffer, dataSize);

		status_t result = B_OK;
		int32 itemSize = 0;
		if (fixedSize)
			itemSize = dataSize / itemCount;

		if (format == MESSAGE_FORMAT_R5) {
			for (int32 i = 0; i < itemCount; i++) {
				if (!fixedSize) {
					itemSize = *(int32 *)pointer;
					pointer += sizeof(int32);
				}

				result = into->AddData(nameBuffer, type, pointer, itemSize,
					fixedSize, itemCount);

				if (result < B_OK) {
					free(buffer);
					return result;
				}

				if (fixedSize)
					pointer += itemSize;
				else {
					pointer += pad_to_8(itemSize + sizeof(int32))
						- sizeof(int32);
				}
			}
		} else {
			for (int32 i = 0; i < itemCount; i++) {
				if (!fixedSize) {
					itemSize = __swap_int32(*(int32 *)pointer);
					pointer += sizeof(int32);
				}

				swap_data(type, pointer, itemSize, B_SWAP_ALWAYS);
				result = into->AddData(nameBuffer, type, pointer, itemSize,
					fixedSize, itemCount);

				if (result < B_OK) {
					free(buffer);
					return result;
				}

				if (fixedSize)
					pointer += itemSize;
				else {
					pointer += pad_to_8(itemSize + sizeof(int32))
						- sizeof(int32);
				}
			}
		}

		free(buffer);

		// flags of next field or termination byte
		reader(flags);
	}

	return B_OK;
}


/*static*/ status_t
MessageAdapter::_UnflattenDanoMessage(uint32 format, BMessage *into,
	BDataIO *stream)
{
	into->MakeEmpty();

	TReadHelper reader(stream);
	if (format == MESSAGE_FORMAT_DANO_SWAPPED)
		reader.SetSwap(true);

	ssize_t size;
	reader(size);

	dano_message_header header;
	reader(header);
	into->what = header.what;

	size -= sizeof(dano_section_header) + sizeof(dano_message_header);
	int32 offset = 0;

	while (offset < size) {
		dano_section_header sectionHeader;
		reader(sectionHeader);

		// be safe. this shouldn't be necessary but in some testcases it was.
		sectionHeader.size = pad_to_8(sectionHeader.size);

		if (offset + sectionHeader.size > size || sectionHeader.size < 0)
			return B_BAD_DATA;

		ssize_t fieldSize = sectionHeader.size - sizeof(dano_section_header);
		uint8 *fieldBuffer = NULL;
		if (fieldSize <= 0) {
			// there may be no data. we shouldn't fail because of that
			offset += sectionHeader.size;
			continue;
		}

		fieldBuffer = (uint8 *)malloc(fieldSize);
		if (fieldBuffer == NULL)
			throw (status_t)B_NO_MEMORY;

		reader(fieldBuffer, fieldSize);

		switch (sectionHeader.code) {
			case SECTION_OFFSET_TABLE:
			case SECTION_TARGET_INFORMATION:
			case SECTION_SORTED_INDEX_TABLE:
			case SECTION_END_OF_DATA:
				// discard
				break;

			case SECTION_SINGLE_ITEM_DATA:
			{
				dano_single_item *field = (dano_single_item *)fieldBuffer;

				int32 dataOffset = sizeof(dano_single_item)
					+ field->name_length + 1;
				dataOffset = pad_to_8(dataOffset);

				if (offset + dataOffset + field->item_size > size)
					return B_BAD_DATA;

				// support for fixed size is not possible with a single item
				bool fixedSize = false;
				switch (field->type) {
					case B_RECT_TYPE:
					case B_POINT_TYPE:
					case B_INT8_TYPE:
					case B_INT16_TYPE:
					case B_INT32_TYPE:
					case B_INT64_TYPE:
					case B_BOOL_TYPE:
					case B_FLOAT_TYPE:
					case B_DOUBLE_TYPE:
					case B_POINTER_TYPE:
					case B_MESSENGER_TYPE:
						fixedSize = true;
						break;
					default:
						break;
				}

				status_t result = into->AddData(field->name, field->type,
					fieldBuffer + dataOffset, field->item_size, fixedSize);

				if (result != B_OK) {
					free(fieldBuffer);
					throw result;
				}
				break;
			}

			case SECTION_FIXED_SIZE_ARRAY_DATA: {
				dano_fixed_size_array *field
					= (dano_fixed_size_array *)fieldBuffer;

				int32 dataOffset = sizeof(dano_fixed_size_array)
					+ field->name_length + 1;
				dataOffset = pad_to_8(dataOffset);
				int32 count = *(int32 *)(fieldBuffer + dataOffset);
				dataOffset += 8; /* count and padding */

				if (offset + dataOffset + count * field->size_per_item > size)
					return B_BAD_DATA;

				status_t result = B_OK;
				for (int32 i = 0; i < count; i++) {
					result = into->AddData(field->name, field->type,
						fieldBuffer + dataOffset, field->size_per_item, true,
						count);

					if (result != B_OK) {
						free(fieldBuffer);
						throw result;
					}

					dataOffset += field->size_per_item;
				}
				break;
			}

			case SECTION_VARIABLE_SIZE_ARRAY_DATA: {
				dano_variable_size_array *field
					= (dano_variable_size_array *)fieldBuffer;

				int32 dataOffset = sizeof(dano_variable_size_array)
					+ field->name_length + 1;
				dataOffset = pad_to_8(dataOffset);
				int32 count = *(int32 *)(fieldBuffer + dataOffset);
				dataOffset += sizeof(int32);
				ssize_t totalSize = *(ssize_t *)(fieldBuffer + dataOffset);
				dataOffset += sizeof(ssize_t);

				int32 *endPoints = (int32 *)(fieldBuffer + dataOffset
					+ totalSize);

				status_t result = B_OK;
				for (int32 i = 0; i < count; i++) {
					int32 itemOffset = (i > 0 ? pad_to_8(endPoints[i - 1]) : 0);

					result = into->AddData(field->name, field->type,
						fieldBuffer + dataOffset + itemOffset,
						endPoints[i] - itemOffset, false, count);

					if (result != B_OK) {
						free(fieldBuffer);
						throw result;
					}
				}
				break;
			}
		}

		free(fieldBuffer);
		offset += sectionHeader.size;
	}

	return B_OK;
}


} // namespace BPrivate
